#!perl
sub upperc {
    local($_) = pop(@_);
    tr/[a-z]/[A-Z]/;
    return $_;
}

&output_header;

&arith_insn("add", "i u ul l p", "x86_64_arith3", "0x03", "T");
&arith_insn("sub", "i u ul l p", "x86_64_arith3", "0x2b", "T");
&arith_insn("mul", "u ul", "(arith_op3)x86_64_mul", "0 /*unsigned*/", "0");
&arith_insn("mul", "i l", "(arith_op3)x86_64_mul", "1 /*signed*/", "0");
&arith_insn("div", "u ul i l", "x86_64_div_mod", "1", "T");
&arith_insn("mod", "u ul i l", "x86_64_div_mod", "0", "T");
&arith_insn("and", "i u ul l", "x86_64_arith3", "0x23", "T");
&arith_insn("or", "i u ul l", "x86_64_arith3", "0x0b", "T");
&arith_insn("xor", "i u ul l", "x86_64_arith3", "0x33", "T");
&arith2_insn("com", "i u ul l", "x86_64_arith2", "0xf7", "2");
&arith2_insn("neg", "i u ul l", "x86_64_arith2", "0xf7", "3");
&arith2_insn("not", "i u ul l", "x86_64_arith2", dill_jmp_noti, "0");
&arith_insn("lsh", "i u ul l", "x86_64_shift", "0x4", "T");
&arith_insn("rsh", "i l", "x86_64_shift", "0x7", "T");
&arith_insn("rsh", "u ul", "x86_64_shift", "0x5", "T");
&arith_insn("add", "f d", "x86_64_farith", "0x58", "T");
&arith_insn("sub", "f d", "x86_64_farith", "0x5c", "T");
&arith_insn("mul", "f d", "x86_64_farith", "0x59", "T");
&arith_insn("div", "f d", "x86_64_farith", "0x5e", "T");
&arith2_insn("neg", "f d", "x86_64_farith2", "0x5c", "T");
&arith2_insn("bswap", "s us i u ul l f d", "x86_64_bswap", "0", "T");

&arithi_insn("add", "i u ul l p", "x86_64_arith3i", "0x0", "T");
&arithi_insn("sub", "i u ul l p", "x86_64_arith3i", "0x05", "T");
&arithi_insn("mul", "u ul", "(arith_op3i)x86_64_mul", "0", "1");
&arithi_insn("mul", "i l", "(arith_op3i)x86_64_mul", "1", "1");
&arithi_insn("div", "u ul i l", "x86_64_div_modi", "1", "T");
&arithi_insn("mod", "u ul i l", "x86_64_div_modi", "0", "T");
&arithi_insn("and", "i u ul l", "x86_64_arith3i", "0x4", "T");
&arithi_insn("or", "i u ul l", "x86_64_arith3i", "0x1", "T");
&arithi_insn("xor", "i u ul l", "x86_64_arith3i", "0x06", "T");
&arithi_insn("lsh", "i u ul l", "x86_64_shifti", "0x4", "T");
&arithi_insn("rsh", "i l", "x86_64_shifti", "0x7", "T");
&arithi_insn("rsh", "u ul", "x86_64_shifti", "0x5", "T");
&branch_insn( "eq ge gt le lt ne", "c uc s us i u ul l p d f", "x86_64_branch");
&branchi_insn( "eq ge gt le lt ne", "c uc s us i u ul l p", "x86_64_branchi");
&compare_insn( "eq ge gt le lt ne", "c uc s us i u ul l p d f", "x86_64_compare");
&comparei_insn( "eq ge gt le lt ne", "c uc s us i u ul l p", "x86_64_comparei");

print COUT "jmp_table ${mach}_jump_table;\n";
print COUT "extern void dill_x86_64_init(dill_stream s)\n{\n";
print COUT "\tif(x86_64_jump_table == 0) {\n";
print COUT "\t  x86_64_jump_table = alloc_dill_jump_table();\n";
print COUT $jmp_a3_assigns;
print COUT $jmp_a3i_assigns;
print COUT $jmp_a2_assigns;
print COUT $jmp_b_assigns;
print COUT $jmp_c_assigns;
print COUT "\t  ${mach}_jump_table->proc_start = (dill_mach_proc_start) x86_64_proc_start;\n";
print COUT "\t  ${mach}_jump_table->end = x86_64_end;\n";
print COUT "\t  ${mach}_jump_table->package_end = x86_64_package_end;\n";
print COUT "\t  ${mach}_jump_table->clone_code = x86_64_clone_code;\n";
print COUT "\t  ${mach}_jump_table->type_align = x86_64_type_align;\n";
print COUT "\t  ${mach}_jump_table->type_size = x86_64_type_size;\n";
print COUT "\t  ${mach}_jump_table->ret = x86_64_ret;\n";
print COUT "\t  ${mach}_jump_table->reti = x86_64_reti;\n";
print COUT "\t  ${mach}_jump_table->retf = (ret_opf)x86_64_retf;\n";
print COUT "\t  ${mach}_jump_table->load = x86_64_pload;\n";
print COUT "\t  ${mach}_jump_table->bsload = x86_64_pbsload;\n";
print COUT "\t  ${mach}_jump_table->loadi = x86_64_ploadi;\n";
print COUT "\t  ${mach}_jump_table->bsloadi = x86_64_pbsloadi;\n";
print COUT "\t  ${mach}_jump_table->loadi = x86_64_ploadi;\n";
print COUT "\t  ${mach}_jump_table->store = x86_64_pstore;\n";
print COUT "\t  ${mach}_jump_table->storei = x86_64_pstorei;\n";
print COUT "\t  ${mach}_jump_table->convert = x86_64_convert;\n";
print COUT "\t  ${mach}_jump_table->mov = x86_64_mov;\n";
print COUT "\t  ${mach}_jump_table->set = x86_64_pset;\n";
print COUT "\t  ${mach}_jump_table->setf = x86_64_setf;\n";
print COUT "\t  ${mach}_jump_table->setp = x86_64_setp;\n";
print COUT "\t  ${mach}_jump_table->jv = x86_64_jump_to_label;\n";
print COUT "\t  ${mach}_jump_table->jp = x86_64_jump_to_reg;\n";
print COUT "\t  ${mach}_jump_table->jpi = x86_64_jump_to_imm;\n";
print COUT "\t  ${mach}_jump_table->jal = x86_64_jal;\n";
print COUT "\t  ${mach}_jump_table->special = x86_64_special;\n";
print COUT "\t  ${mach}_jump_table->push = x86_64_push;\n";
print COUT "\t  ${mach}_jump_table->pushi = x86_64_pushi;\n";
print COUT "\t  ${mach}_jump_table->pushpi = x86_64_pushpi;\n";
print COUT "\t  ${mach}_jump_table->pushfi = x86_64_pushfi;\n";
print COUT "\t  ${mach}_jump_table->calli = x86_64_calli;\n";
print COUT "\t  ${mach}_jump_table->callr = x86_64_callr;\n";
print COUT "\t  ${mach}_jump_table->local = x86_64_local_op;\n";
print COUT "\t  ${mach}_jump_table->save_restore = x86_64_save_restore_op;\n";
#print COUT "	${mach}_jump_table->lea = x86_64_lea;\n";
print COUT "\t  ${mach}_jump_table->init_disassembly = x86_64_init_disassembly_info;\n";
print COUT "\t  ${mach}_jump_table->print_insn = x86_64_print_insn;\n";
print COUT "\t  ${mach}_jump_table->print_reg = x86_64_print_reg;\n";
print COUT "\t  ${mach}_jump_table->count_insn = x86_64_count_insn;\n";
print COUT "\t  ${mach}_jump_table->do_reverse_push = 0;\n";
print COUT "\t  ${mach}_jump_table->target_byte_order = 2;  /* Format_Integer_littleendian */\n";
print COUT "\t  ${mach}_jump_table->target_float_format = 2;  /* Format_IEEE_754_littleendian */ \n";
print COUT "\t}\n";
print COUT "\ts->j = x86_64_jump_table;\n";
print COUT "\ts->dill_local_pointer = EBP;\n";
print COUT "\ts->dill_param_reg_pointer = EBP;\n";
print COUT "\ts->p->mach_info = (void*)gen_x86_64_mach_info(s);\n";
print COUT "\ts->p->machine_strr_tmp_reg = EAX;\n";
print COUT "\treturn;\n";
print COUT "}\n";

sub arith_insn {
    local ($op, $type_list, $subr, $code1, $code2, $pcode1, $pcode2) = @_;
    foreach(split(' ', $type_list)) {
	$pcode2 = $code2;
	if ($code2 eq "T") {
	    $pcode2 = "DILL_". &upperc(${_});
	}
	$jmp_a3_assigns = $jmp_a3_assigns . "\t  ${mach}_jump_table->jmp_a3[dill_jmp_${op}${_}] = $subr;\n";
	$jmp_a3_assigns = $jmp_a3_assigns . "\t  ${mach}_jump_table->a3_data[dill_jmp_${op}${_}].data1 = $code1;\n";
	$jmp_a3_assigns = $jmp_a3_assigns . "\t  ${mach}_jump_table->a3_data[dill_jmp_${op}${_}].data2 = $pcode2;\n";
    }
}

sub arith2_insn {
    local ($op, $type_list, $subr, $code1, $code2) = @_;
    foreach(split(' ', $type_list)) {
	$pcode2 = $code2;
	if ($code2 eq "T") {
	    $pcode2 = "DILL_". &upperc(${_});
	}
	$jmp_a2_assigns = $jmp_a2_assigns . "\t  ${mach}_jump_table->jmp_a2[dill_jmp_${op}${_}] = $subr;\n";
	$jmp_a2_assigns = $jmp_a2_assigns . "\t  ${mach}_jump_table->a2_data[dill_jmp_${op}${_}].data1 = $code1;\n";
	$jmp_a2_assigns = $jmp_a2_assigns . "\t  ${mach}_jump_table->a2_data[dill_jmp_${op}${_}].data2 = $pcode2;\n";
    }
}

sub arithi_insn {
    local ($op, $type_list, $subr, $code1, $code2, $pcode2) = @_;
    $pcode2 = $code2;
    foreach(split(' ', $type_list)) {
	if ($code2 eq "T") {
	    $pcode2 = "DILL_". &upperc(${_});
	}
	$jmp_a3i_assigns = $jmp_a3i_assigns . "\t  ${mach}_jump_table->jmp_a3i[dill_jmp_${op}${_}] = $subr;\n";
	$jmp_a3i_assigns = $jmp_a3i_assigns . "\t  ${mach}_jump_table->a3i_data[dill_jmp_${op}${_}].data1 = $code1;\n";
	$jmp_a3i_assigns = $jmp_a3i_assigns . "\t  ${mach}_jump_table->a3i_data[dill_jmp_${op}${_}].data2 = $pcode2;\n";
    }
}

sub branch_insn {
    local($ops, $types, $subr) = @_;
    foreach (split(' ', $ops)) {
	$op = $_;
	foreach (split(' ', $types)) {
	    $jmp_b_assigns = $jmp_b_assigns . "\t  ${mach}_jump_table->jmp_b[dill_jmp_b${op}${_}] = $subr;\n";
	    $jmp_b_assigns = $jmp_b_assigns . "\t  ${mach}_jump_table->b_data[dill_jmp_b${op}${_}].data1 = dill_${op}_code;\n";
	    $jmp_b_assigns = $jmp_b_assigns . "\t  ${mach}_jump_table->b_data[dill_jmp_b${op}${_}].data2 = DILL_". &upperc(${_}). ";\n";
	}
    }
}

sub branchi_insn {
    local($ops, $types, $subr) = @_;
    foreach (split(' ', $ops)) {
	$op = $_;
	foreach (split(' ', $types)) {
	    $jmp_b_assigns = $jmp_b_assigns . "\t  ${mach}_jump_table->jmp_bi[dill_jmp_b${op}${_}] = $subr;\n";
	}
    }
}

sub compare_insn {
    local($ops, $types, $subr) = @_;
    foreach (split(' ', $ops)) {
	$op = $_;
	foreach (split(' ', $types)) {
	    $jmp_c_assigns = $jmp_c_assigns . "\t  ${mach}_jump_table->jmp_c[dill_jmp_c${op}${_}] = $subr;\n";
	    $jmp_c_assigns = $jmp_c_assigns . "\t  ${mach}_jump_table->c_data[dill_jmp_c${op}${_}].data1 = dill_${op}_code;\n";
	    $jmp_c_assigns = $jmp_c_assigns . "\t  ${mach}_jump_table->c_data[dill_jmp_c${op}${_}].data2 = DILL_". &upperc(${_}). ";\n";
	}
    }
}

sub comparei_insn {
    local($ops, $types, $subr) = @_;
    foreach (split(' ', $ops)) {
	$op = $_;
	foreach (split(' ', $types)) {
	    $jmp_c_assigns = $jmp_c_assigns . "\t  ${mach}_jump_table->jmp_ci[dill_jmp_c${op}${_}] = $subr;\n";
	}
    }
}

sub output_header {
    $mach = x86_64;
    open(COUT, ">dill_${mach}.c") || die "Can't open header output";
print COUT<<EOF;
/* This file is generated from x86_64.ops.  Do not edit directly. */

#include "config.h"
#include "dill.h"
#include "dill_internal.h"
#include "x86_64.h"
EOF
}
